/**
 * 
 */
package net.svenbuschbeck.gwt.ao.client;

import net.svenbuschbeck.gwt.ao.client.events.ValueChangedEvent;
import net.svenbuschbeck.gwt.ao.client.events.ValueChangedHandler;

/**
 * 
 * 
 * @author Sven Buschbeck
 * @param <T>
 * 
 */
public class ActivePoint<T> extends ChangedHandler<ActivePoint.Point<T>> {

	public static abstract class Point<T> {
		public abstract T getX();

		public abstract T getY();

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#toString()
		 */
		@Override public String toString() {
			return this.getClass().getName() + "(x=" + getX() + ", y=" + getY() + ")";
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#equals(java.lang.Object)
		 */
		@Override public boolean equals(Object obj) {
			if (obj != null && obj instanceof ActivePoint.Point<?>) {
				ActivePoint.Point<?> point = (ActivePoint.Point<?>) obj;
				return point.getX() == getX() && point.getY() == getY();
			}
			return false;
		}
	}

	public final ActiveVariable<T> x = new ActiveVariable<T>();
	public final ActiveVariable<T> y = new ActiveVariable<T>();
	public boolean ignoreValueChanged = false;

	public ActivePoint() {
		this(null, null);
	}

	public ActivePoint(T nx, T ny) {
		set(nx, ny);
		x.addValueChangedHandler(new ValueChangedHandler<T>() {

			@Override public void onValueChanged(ValueChangedEvent<T> event) {
				fireEvent(createPoint(event.getOldValue(), y.get()), createPoint(event.getNewValue(), y.get()));
			}
		});
		y.addValueChangedHandler(new ValueChangedHandler<T>() {

			@Override public void onValueChanged(ValueChangedEvent<T> event) {
				fireEvent(createPoint(x.get(), event.getOldValue()), createPoint(x.get(), event.getNewValue()));
			}
		});
	}

	public void set(Point<T> point) {
		set(point, false);
	}

	public void set(Point<T> point, boolean forceUpdate) {
		set(point.getX(), point.getY());
	}

	public void set(T x, T y) {
		set(x, y, false);
	}

	public void set(T x, T y, boolean forceUpdate) {
		Point<T> oldValue = this.get();
		ignoreValueChanged = true;
		this.x.set(x, forceUpdate);
		this.y.set(y, forceUpdate);
		value = this.get();
		ignoreValueChanged = false;
		fireEvent(oldValue, this.get(), forceUpdate);
	}

	private void fireEvent(Point<T> oldPoint, Point<T> newPoint) {
		fireEvent(oldPoint, newPoint, false);
	}

	private void fireEvent(Point<T> oldPoint, Point<T> newPoint, boolean forceUpdate) {
		if (!ignoreValueChanged) {
			ValueChangedEvent.fireIfNotEqual(this, oldPoint, newPoint, forceUpdate);
		}
	}

	public Point<T> get() {
		return createPoint(x.get(), y.get());
	}

	public static <X> Point<X> createPoint(final X cx, final X cy) {
		return new Point<X>() {

			@Override public X getX() {
				return cx;
			}

			@Override public X getY() {
				return cy;
			}

		};
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override public boolean equals(Object other) {
		try {
			return other != null && other instanceof ActivePoint<?> && equals((ActivePoint<T>) other);
		} catch (Throwable t) {
			return false;
		}
	}

	public boolean equals(ActivePoint<T> other) {
		return other != null && other.x.equals(x) && other.y.equals(y);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.svenbuschbeck.gwt.ao.client.ChangedHandler#toString()
	 */
	@Override public String toString() {
		return super.toString("x=" + x + "; y=" + y);
	}

}
